package com.leco.demo.spring;

import com.leco.demo.spring.config.Config;
import com.leco.demo.spring.processor.ComponentScanProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author greg
 * @version 2022/10/23
 * ComponentScan实现源码梳理
 **/
public class ComponentScanDemo {
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("config", Config.class);
        context.registerBean(ComponentScanProcessor.class);

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }

    private static void processComponentScan(GenericApplicationContext context) throws IOException {
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
        CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan != null) {
            for (String p : componentScan.basePackages()) {
    //            System.out.println(p);
                String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
    //            System.out.println(path);

                Resource[] resources = context.getResources(path);
                for (Resource resource : resources) {
    //                System.out.println(resource.getFile());
                    MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
    //                System.out.println(metadataReader.getClassMetadata().getClassName());
                    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                    boolean hasComponent = annotationMetadata.hasAnnotation(Component.class.getName());
                    boolean hasMetaComponent = annotationMetadata.hasMetaAnnotation(Component.class.getName());
    //                System.out.println(hasComponent);
    //                System.out.println(hasMetaComponent);
                    if (hasComponent || hasMetaComponent) {
                        AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();
                        String beanName = generator.generateBeanName(bd, beanFactory);
                        beanFactory.registerBeanDefinition(beanName, bd);
                    }

                }
            }

        }
    }
}
